#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include "vppion.h"
#include "vpplib.h"
#include <sys/time.h>

#define CONV_YUV420P_TO_BGR     ((FMT_I420) | (FMT_BGR << 4))
#define CONV_NV12_TO_BGR        ((FMT_NV12) | (FMT_BGR << 4))
#define CONV_YUV420P_TO_RGB     ((FMT_I420) | (FMT_RGB << 4))
#define CONV_NV12_TO_RGB        ((FMT_NV12) | (FMT_RGB << 4))
#define CONV_NV12_TO_YUV420P    ((FMT_NV12) | (FMT_I420 << 4))
#define CONV_YUV420P_TO_YUV420P    ((FMT_I420) | (FMT_I420 << 4))

int main(int argc, char *argv[])
{
    int src_fmt, dst_fmt;
    ion_dev_fd_s ion_dev_fd;
    int vpp_dev_fd;
    int convert_fmt;
    int width , height;
    int is_src_pa, is_dst_pa;
    int sx, sy, cw, ch, dw, dh;
    vpp_mat src;
    vpp_mat dst;
    char *img_name;
    char *input_name;
    vpp_rect rect;
    struct timeval tv1, tv0;

    if (argc != 15)
    {
        printf("usage: %d\n", argc);
        printf("%s width height src_img_name is_src_pa is_dst_pa src-format dest-format sx sy cw ch dw dh dst_img_name\n", argv[0]);
        printf("    format: 0 -- yuv420p)\n");
        printf("            7 -- nv12)\n");
        printf("            5 -- bgr)\n");
        printf("            6 -- rgb)\n");
        printf("EX\n");
        printf("    yuv420p -> bgr:     %s 1920 1080 img2.yuv 0 0 0 5 0 0 960 540 960 540 out.bmp\n", argv[0]);
        printf("    nv12    -> bgr:     %s 1920 1080 img1.nv12 0 0 7 5 0 0 960 540 960 540 out.bmp\n", argv[0]);
        printf("    yuv420p -> rgb:     %s 1920 1080 img2.yuv 0 0 0 6 0 0 960 540 960 540 out.bmp\n", argv[0]);
        printf("    nv12    -> rgb:     %s 1920 1080 img1.nv12 0 0 7 6 0 0 960 540 960 540 out.bmp\n", argv[0]);
        printf("    nv12    -> yuv420p: %s 1920 1080 img1.nv12 0 0 7 0 0 0 960 540 960 540 out.yuv\n", argv[0]);
        printf("    yuv420p    -> yuv420p: %s 1920 1080 img2.yuv 0 0 0 0 0 0 960 540 960 540 out.yuv\n", argv[0]);
        return 0;
    }

    width = atoi(argv[1]);
    height = atoi(argv[2]);

    input_name = argv[3];
    is_src_pa = atoi(argv[4]);
    is_dst_pa = atoi(argv[5]);

    src_fmt = atoi(argv[6]);
    dst_fmt = atoi(argv[7]);

    sx = atoi(argv[8]);
    sy = atoi(argv[9]);
    cw = atoi(argv[10]);
    ch = atoi(argv[11]);
    dw = atoi(argv[12]);
    dh = atoi(argv[13]);

    VppAssert((FMT_I420 == src_fmt) || (FMT_NV12 == src_fmt));
    VppAssert((FMT_BGR == dst_fmt) || (FMT_RGB == dst_fmt) || (FMT_I420== dst_fmt));
    VppAssert((width > 0) && (height > 0));
    VppAssert((sx >= 0) && (sy >= 0));
    VppAssert((cw > 0) && (ch > 0));
    VppAssert((dw > 0) && (dh > 0));

    img_name = argv[14];

    VppAssert((is_src_pa == 0) || (is_src_pa == 1));
    VppAssert((is_dst_pa == 0) || (is_dst_pa == 1));

    ion_dev_fd.dev_fd = open("/dev/ion", O_RDWR | O_DSYNC);
    if (ion_dev_fd.dev_fd < 0) {
        VppErr("open /dev/ion failed, errno = %d, msg: %s\n", errno, strerror(errno));
        return VPP_ERR;
    }
    memcpy(ion_dev_fd.name,"ion",sizeof("ion"));

    vpp_dev_fd = open("/dev/bm-vpp", O_RDWR | O_DSYNC);
    if (vpp_dev_fd < 0) {
        VppErr("open /dev/bm-vpp failed, errno = %d, msg: %s\n", errno, strerror(errno));
        return VPP_ERR;
    }

    vpp_init_lib();

    if (vpp_creat_ion_mem_fd(&src, src_fmt, width, height, &ion_dev_fd) == VPP_ERR) {
        goto err0;
    }

    src.vpp_fd.dev_fd = vpp_dev_fd;
    src.vpp_fd.name = "bm-vpp";
    src.is_pa = is_src_pa;

    rect.x = sx;
    rect.y = sy;
    rect.width= cw;
    rect.height= ch;

    if (vpp_creat_ion_mem_fd(&dst, dst_fmt, dw, dh, &ion_dev_fd) == VPP_ERR) {
        goto err1;
    }
    dst.is_pa = is_dst_pa;

    if (vpp_read_file(&src, &ion_dev_fd, input_name) < 0)
    {
        goto err2;
    }

    convert_fmt = src_fmt | (dst_fmt << 4);

    switch (convert_fmt)
    {
        case CONV_YUV420P_TO_BGR:
        case CONV_NV12_TO_BGR:
        case CONV_YUV420P_TO_RGB:
        case CONV_NV12_TO_RGB:
            ion_invalidate(&ion_dev_fd, dst.va[0], dst.ion_len[0]);

            gettimeofday(&tv0, NULL);
            vpp_misc(&src, &rect, &dst, 0);
            gettimeofday(&tv1, NULL);
            int t = (tv1.tv_sec - tv0.tv_sec) * 1000000 + (tv1.tv_usec - tv0.tv_usec);
            printf("vpp_yuv420_to_rgb: %d (us)\n", t);

            vpp_bmp_bgr888(img_name, (unsigned  char *)dst.va[0], dst.cols, dst.rows, dst.stride);
            break;
        case CONV_YUV420P_TO_YUV420P:
        case CONV_NV12_TO_YUV420P:
            ion_invalidate(&ion_dev_fd, dst.va[0], dst.ion_len[0]);
            ion_invalidate(&ion_dev_fd, dst.va[1], dst.ion_len[1]);
            ion_invalidate(&ion_dev_fd, dst.va[2], dst.ion_len[2]);

            vpp_misc(&src, &rect, &dst, 0);

            vpp_output_mat_to_yuv420(img_name, &dst);
            break;
        default:
            printf("not supported: src_fmt = %d, dest_fmt = %d\n", src_fmt, dst_fmt);
            break;
    }

err2:
    vpp_free_ion_mem(&dst);
err1:
    vpp_free_ion_mem(&src);
err0:
    close(ion_dev_fd.dev_fd);
    close(vpp_dev_fd);

    return 0;
}
